package com.mspbots.rule.exceptions.cw;

import cn.hutool.core.util.StrUtil;
import com.mspbots.api.service.TenantService;
import com.mspbots.api.service.TenantUserTimeEntryService;
import com.mspbots.cw.request.MessagesDTO;
import com.mspbots.cw.request.TenantDTO;
import com.mspbots.cw.request.TenantTimeEntryDTO;
import com.mspbots.cw.request.TenantTimeLogDTO;
import com.mspbots.rule.exceptions.BaseRule;
import lombok.extern.slf4j.Slf4j;
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.mspbots.cw.request.MessagesDTO.TYPE_USER;

/**
 * com.mspbots.rule.exceptions.timeentry.NotEnoughTimeLogged
 *
 * @author Jun Zhou
 * @date 14:37 2019/7/1
 **/
@Slf4j
@Rule(name = "NotEnoughTimeLogged", description = "Long time log more than 2 hours, with less than 100 letters of time log notes")
public class NotEnoughTimeLogged extends BaseRule {


    protected TenantUserTimeEntryService tenantUserTimeEntryService;
    protected TenantService tenantService;
    protected StringRedisTemplate stringRedisTemplate;

    private List<TenantTimeEntryDTO> entryDTOList;
    private Long cwLogTime = Long.valueOf(0);


    public NotEnoughTimeLogged() {
    }

    @Condition
    public boolean condition(@Fact(FACT) TenantTimeLogDTO tenantTimeLogDTO) {
        try {
            if (!setUserMapping(tenantTimeLogDTO.getTenantUserId())) {
                log.error("There is no user in teams for tenant_user_id [{}]", tenantTimeLogDTO.getTenantUserId());
                return false;
            }
            int inHours = Duration.between(tenantTimeLogDTO.getBeginDate(), LocalDateTime.now()).toHoursPart();
            if (inHours < 0) {
                return false;
            }

            //temp code
            Map<String, Object> timeEntryParams = new HashMap<>(16) {{
                put("tenantId", tenantRuleDTO.getTenantId());
                put("enteredDateStart", LocalDateTime.now().minusHours(24));
                put("tenantUserId", tenantTimeLogDTO.getTenantUserId());
            }};

            List<TenantTimeEntryDTO> entryDTOList = tenantUserTimeEntryService.listByTenantUserId(timeEntryParams);
            this.entryDTOList = entryDTOList;
            return getTeamsLogTime(tenantTimeLogDTO) / 60 - 1.5 > getCWLogTime(entryDTOList) / 60;
        } catch (Exception e) {
            log.error("NotEnoughTimeLogged rule execute error [{}] : {}", e.getStackTrace()[0].getLineNumber(), e.getMessage());
        }
        return false;
    }

    private Long getTeamsLogTime(TenantTimeLogDTO timeLogDTO) {

        Long workMinutes = timeLogDTO.getEndDate() == null
                ? Duration.between(timeLogDTO.getBeginDate(), LocalDateTime.now()).toMinutes()
                : Duration.between(timeLogDTO.getBeginDate(), timeLogDTO.getEndDate()).toMinutes();
        // minus lunch
        if (timeLogDTO.getLunchDate() != null && timeLogDTO.getBackLunchDate() != null) {
            workMinutes = workMinutes - Duration.between(timeLogDTO.getLunchDate(), timeLogDTO.getBackLunchDate())
                    .toMinutes();
        }
        // minus break
        if (timeLogDTO.getBreakMap() != null) {
            Long breakMinutes = timeLogDTO.getBreakMap()
                    .stream()
                    .filter(item -> item.get("endTime") != null)
                    .mapToLong(
                            item -> Duration.between(LocalDateTime.parse(item.get("beginTime").toString()),
                                    LocalDateTime.parse(item.get("endTime").toString())).toMinutes())
                    .sum();
            workMinutes = workMinutes - breakMinutes;
        }
        return workMinutes;
    }

    private Long getCWLogTime(List<TenantTimeEntryDTO> entryDTOList) {
        long cwLogTime = entryDTOList.isEmpty()
                ? 0
                : Duration.between(entryDTOList.get(0).getBeginDate(), entryDTOList.get(0).getEndDate()).toMinutes();
        for (int i = 1; i < entryDTOList.size(); i++) {
            //overlap
            if (entryDTOList.get(i).getBeginDate().isBefore(entryDTOList.get(i - 1).getEndDate())) {
                // eg: 1:00 - 4:00  and 2:00 - 3:00  don't sum
                if (entryDTOList.get(i).getEndDate().isAfter(entryDTOList.get(i - 1).getEndDate())) {
                    cwLogTime = Duration.between(entryDTOList.get(i - 1).getEndDate(), entryDTOList.get(i).getEndDate())
                            .toMinutes();
                }
            } else {
                cwLogTime += Duration.between(entryDTOList.get(i).getBeginDate(), entryDTOList.get(i).getEndDate())
                        .toMinutes();
            }
        }

        this.cwLogTime = cwLogTime / 60;
        return cwLogTime;
    }

    @Action
    public void action(@Fact(FACT) TenantTimeLogDTO tenantTimeLogDTO) {


        TenantDTO tenantDTO = tenantService.findById(tenantTimeLogDTO.getTenantId());
        MessagesDTO messages = new MessagesDTO();
        messages.setUserId(tenantTimeLogDTO.getTenantUserId());
        if (entryDTOList.isEmpty()) {
            messages.setTimeEntryId(Long.valueOf(0));
        } else {
            messages.setTimeEntryId(entryDTOList.get(0).getCwTid());
        }
        messages.setTeamsUserId(tenantUserMappingDTO.getTeamsUserId());
        messages.setRuleId(this.tenantRuleDTO.getRuleId());
        messages.setTenantId(tenantTimeLogDTO.getTenantId());
        messages.setStatus("Ready");
        messages.setForwardStatus("Ready");
        messages.setType(TYPE_USER);
        messages.setCreateDate(LocalDateTime.now());
        messages.setExpireDate(messages.getCreateDate().plusHours(12));

        String template = StringUtils.isEmpty(this.tenantRuleDTO.getTemplate()) ?
                this.tenantRuleDTO.getSysTemplate() : this.tenantRuleDTO.getTemplate();
        Map<String, Object> params = new HashMap<>();
        params.put("USER", tenantUserMappingDTO.getFirstName());
        params.put("INHOUR", Duration.between(tenantTimeLogDTO.getBeginDate(), LocalDateTime.now()).toHoursPart());
        params.put("LOGHOUR", this.cwLogTime);
        params.put("TRAIN_LINK", "<a href='https://app.mspbots.ai'>[this training link]</a>");
        params.put("COMP", tenantDTO.getName());
        params.put("VIDEO_URL", "<a href='https://app.mspbots.ai'>video URL</a>");
        String message = StrUtil.format(template, params);

        messages.setMessage(message + "<br>" + suffixEscalation() + suffixMessage(LocalDateTime.now()));
//        messages.setBusinessType();
//        messages.setBusinessId();
        messagesService.insert(messages);


    }


    public void setTenantUserTimeEntryService(TenantUserTimeEntryService tenantUserTimeEntryService) {
        this.tenantUserTimeEntryService = tenantUserTimeEntryService;
    }

    public void setTenantService(TenantService tenantService) {
        this.tenantService = tenantService;
    }

    @Override
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

}
